﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace CorporationWars
{
    [Serializable]
    public class BaseNode : BaseObject
    {
        public NodeEvent OnExit;
        public string CorperationID { get; set; }
        public decimal Cost { get; set; }
        public string Name { get; set; }

        public string ShortName { get; set; }
        public string Description { get; set; }

        public bool IsVisibleFromOtherNodes { get; set; }

        public bool HasExternalAccess { get; set; }

        public List<NodeConnection> Connections { get; set; }

        public List<Software> Software { get; set; }

        public Vector3 Position { get; set; }

        public float distanceFromCamera { get; set; }

        public List<SalaryLevels> PersonelRequired { get; set; }

        public const int ButtonMaxY = 488;
        public const int MaxXButtons = 6;
        public const int ButtonSize = 64;
        public const int ButtonRowOneY = 360;

        SpriteBatch spriteBatch { get { return (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch)); } }

        public BaseNode(Game1 game)
            : base(game)
        {
            Name = ID;
            Cost = 0;
            IsVisibleFromOtherNodes = true;
            HasExternalAccess = false;

            Connections = new List<NodeConnection>();
            Software = new List<Software>();

            Buttons = new List<UIButton>();
            ScreenButtons = new Dictionary<string, List<UIButton>>();
        }

        public BaseNode(Game1 game, string name, decimal cost)
            : this(game)
        {
            Name = name;
            Cost = cost;

            // Exit node button
            AddButton(new UIButton(game) { TextureAsset = "Textures/HUD/Buttons/Back", Color = Color.LimeGreen, HoverColor = Color.Lime, MouseDownColor = Color.Red, Name = "ExitNode", OnClick = ButtonClicked, ToolTipText = "Exit Node" });
        }

        public void ConnectTo(BaseNode node, bool visible)
        {
            if (!AmIConnectedTo(node))
            {
                Connections.Add(new NodeConnection(Game,new NodePoint(Game,this, true), new NodePoint(Game,node, visible)));
                node.ConnectTo(this, IsVisibleFromOtherNodes);
            }
        }

        public bool AmIConnectedTo(BaseNode node)
        {
            List<BaseNode> whoImConnectedTo = WhoIAmConnectedTo(true);

            var connected = whoImConnectedTo.Where(element => element.ID == node.ID);

            return connected.ToArray().Length > 0;
        }

        public void RemoveConnection(BaseNode node)
        {
            List<BaseNode> whoImConnectedTo = WhoIAmConnectedTo(true);

            var connected = whoImConnectedTo.Where(element => element.ID == node.ID);
            if (connected.ToArray().Length > 0)
            {
                List<NodeConnection> delList = new List<NodeConnection>();
                foreach (NodeConnection nodeCon in Connections)
                {
                    foreach (NodePoint endPoint in nodeCon.EndPoints)
                    {
                        if (endPoint.Node == node)
                        {
                            delList.Add(nodeCon);
                            break;
                        }
                    }
                }

                foreach (NodeConnection nodeCon in delList)
                {
                    Connections.Remove(nodeCon);
                    nodeCon.CloseConnection();
                }
            }
        }
        
        public List<BaseNode> WhoIAmConnectedTo(bool showHidden = false)
        {
            List<BaseNode> retVal = new List<BaseNode>();

            foreach (NodeConnection nodeCon in Connections)
            {
                foreach (NodePoint endPoint in nodeCon.EndPoints)
                {
                    if (endPoint.Node != this)
                    {
                        if(endPoint.Visible || showHidden)
                            retVal.Add(endPoint.Node);

                        break;
                    }
                }
            }

            return retVal;
        }

        public void CloseNode()
        {
            foreach (NodeConnection connection in Connections)
                connection.CloseConnection();

            Connections.Clear();
            Connections = null;

            foreach (Software sw in Software)
                sw.DeleteSoftware();

            Software.Clear();
            Software = null;
        }

        protected List<UIButton> Buttons { get; set; }
        protected Dictionary<string, List<UIButton>> ScreenButtons { get; set; }

        protected string selectedButton = string.Empty;
        protected string selectedScreenButton = string.Empty;

        public bool IsMyCorp = false;

        public virtual void UpdateNode(GameTime gameTime)
        {
            UpdateDigitalCurtain();

            if (IsMyCorp)
            {
                if (selectedButton == string.Empty || !ScreenButtons.ContainsKey(selectedButton))
                    UpdateList(gameTime, Buttons);
                else
                    UpdateList(gameTime, ScreenButtons[selectedButton]);
            }
            else
            {
                // Do exit node button
                Buttons[0].Update(gameTime);
            }
        }

        protected virtual void UpdateList(GameTime gameTime, List<UIButton> buttonList)
        {
            foreach (UIButton button in buttonList)
                button.Update(gameTime);
        }

        public virtual void DrawNode(GameTime gameTime)
        {
            Game.GraphicsDevice.Clear(Color.Black);

            if (IsMyCorp)
            {
                if (selectedButton == string.Empty || !ScreenButtons.ContainsKey(selectedButton))
                    DrawList(gameTime, Buttons);
                else
                {
                    Color color = Color.Red;
                    if (IsMyCorp)
                        color = Color.White;

                    spriteBatch.Draw(Game.Content.Load<Texture2D>("Textures/textBoxBG"), new Rectangle(412, 48, 540, 512), color);

                    nodeScreenLeft = 422;
                    nodeScreenCenter = 412 + (540 / 2);
                    nodeScreenTop = 62;
                    nodeScreenWidth = 522;
                    nodeScreenHeight = 512;
                    nodeScreenRight = nodeScreenLeft + nodeScreenWidth;

                    DrawList(gameTime, ScreenButtons[selectedButton]);

                }
            }
            else
            {
                // Do all the hacking stuff....

                // Show exit button...
                Buttons[0].Draw(gameTime);
            }
        }

        List<string>[] tears { get; set; }
        List<Vector2[]>[] tearPos { get; set; }
        List<float>[] tearLen { get; set; }

        [NonSerialized]
        SpriteFont font = null;

        float[] ws { get; set; }
        float speed = 4;
        float[] scales = new float[] { 1, .75f, .5f };
        float[] ls { get; set; }

        protected virtual void UpdateDigitalCurtain()
        {
            if (tears == null)
                SetUpDigitalCurtain();

            for (int t = 0; t < tears.Length; t++)
            {
                for (int tl = 0; tl < ws[t]; tl++)
                {
                    for (int vp = 0; vp < 5; vp++)
                    {
                        tearPos[t][tl][vp] += new Vector2(0, speed - t);
                        if (tearPos[t][tl][0].Y - tearLen[t][tl] > Game.GraphicsDevice.Viewport.Height)
                        {
                            tearPos[t][tl][0] = GenTearPos(ls[t] * tl, -tearLen[t][tl]);
                            tearPos[t][tl][1] = tearPos[t][tl][0] - new Vector2(0, tearLen[t][tl] * .125f);
                            tearPos[t][tl][2] = tearPos[t][tl][0] - new Vector2(0, tearLen[t][tl] * .25f);
                            tearPos[t][tl][3] = tearPos[t][tl][0] - new Vector2(0, tearLen[t][tl] * .5f);
                            tearPos[t][tl][4] = tearPos[t][tl][0] - new Vector2(0, tearLen[t][tl] * .75f);
                        }
                    }
                }
            }            
        }
        protected void ClearCurtain()
        {
            tears = null;
            tearPos = null;
            tearLen = null;
            ws = null;        
            ls = null;

            font = null;
        }
        protected void SetUpDigitalCurtain()
        {
            font = Game.Content.Load<SpriteFont>("Fonts/digitalCurtainFont");

            tears = new List<string>[3];
            tearPos = new List<Vector2[]>[3];
            tearLen = new List<float>[3];

            ls = new float[] { font.LineSpacing * scales[0], font.LineSpacing * scales[1], font.LineSpacing * scales[2] };
            ws = new float[] { Game.GraphicsDevice.Viewport.Width / ls[0], Game.GraphicsDevice.Viewport.Width / ls[1], Game.GraphicsDevice.Viewport.Width / ls[2] };


            for (int t = 0; t < tearPos.Length; t++)
            {
                tears[t] = new List<string>();
                tearPos[t] = new List<Vector2[]>();
                tearLen[t] = new List<float>();
                Vector2[] poss = new Vector2[5];
                for (int tl = 0; tl < ws[t]; tl++)
                {
                    tears[t].Add(GenTear());
                    tearLen[t].Add(font.MeasureString(tears[t][tears[t].Count - 1]).X * scales[t]);
                    poss = new Vector2[5];
                    poss[0] = GenTearPos(ls[t] * tears[t].Count);
                    poss[1] = poss[0] - new Vector2(0, tearLen[t][tearLen[t].Count - 1] * .125f);
                    poss[2] = poss[0] - new Vector2(0, tearLen[t][tearLen[t].Count - 1] * .25f);
                    poss[3] = poss[0] - new Vector2(0, tearLen[t][tearLen[t].Count - 1] * .5f);
                    poss[4] = poss[0] - new Vector2(0, tearLen[t][tearLen[t].Count - 1] * .75f);
                    tearPos[t].Add(poss);
                }
            }
        }

        public virtual void DrawDigitalCurtain(Color[] colors)
        {
            if (tears == null)
                SetUpDigitalCurtain();

            for (int t = 0; t < tears.Length; t++)
            {
                for (int tl = 0; tl < ws[t]; tl++)
                {
                    DrawTear(tears[t][tl], tearPos[t][tl], scales[t], colors, spriteBatch, font, tearLen[t][tl]);
                }
            }                        
        }

        protected void DrawTear(string text, Vector2[] poss, float scale, Color[] colors, SpriteBatch spriteBatch, SpriteFont font, float l)
        {
            for (int p = 0; p < 5; p++)
                spriteBatch.DrawString(font, text, poss[p], colors[p], MathHelper.PiOver2, Vector2.Zero, scale, SpriteEffects.None, 0);
        }

        protected Vector2 GenTearPos(float mod, float yoffset = -float.MaxValue)
        {
            float x = 0, y = 0;

            //x = RoundToMultipleOf((int)MathHelper.Lerp(0, Game.GraphicsDevice.Viewport.Width, (float)Rnd.NextDouble()), (int) mod);
            x = mod;
            if (yoffset == -float.MaxValue)
            {
                y = MathHelper.Lerp(0, Game.GraphicsDevice.Viewport.Height, (float)Rnd.NextDouble());
                return new Vector2(x, y);
            }
            else
                return new Vector2(x, yoffset);
        }
       
        protected string GenTear( int len = 0)
        {
            string tear = string.Empty;

            if(len == 0)
                len = Rnd.Next(4, 17);

            for (int c = 0; c < len; c++)
                tear += string.Format("{0}", Rnd.Next(0, 2));

            return tear;
        }

        protected int nodeScreenCenter = 0;
        protected int nodeScreenTop = 0;
        protected int nodeScreenLeft = 0;
        protected int nodeScreenWidth = 0;
        protected int nodeScreenRight = 0;
        protected int nodeScreenHeight = 0;

        protected virtual void DrawList(GameTime gameTime, List<UIButton> buttonList)
        {
            int cnt = buttonList.Count-1;

            for(;cnt >= 0;cnt--)
                buttonList[cnt].Draw(gameTime);
        }

        public void ExitNode()
        {
            selectedButton = string.Empty;
            selectedScreenButton = string.Empty;
            ClearCurtain();

            if (OnExit != null)
                OnExit(this);
        }

        public virtual void ButtonClicked(UIZone sender)
        {
            if (sender.Name == "ExitNode")
            {
                ExitNode();
                return;
            }
            
            selectedButton = sender.Name;
            selectedScreenButton = string.Empty;            
        }

        public virtual void ScreenButtonClicked(UIZone sender)
        {
            if (selectedScreenButton == sender.Name)
                selectedScreenButton = string.Empty;
            else
                selectedScreenButton = sender.Name;
        }

        public virtual void AddButton(UIButton button)
        {
            button.Position = CalcButtonPos(button, Buttons);
            Buttons.Add(button);
        }

        protected Vector2 CalcButtonPos(UIButton button, List<UIButton> buttonList)
        {
            button.Width = ButtonSize;
            button.Height = ButtonSize;

            int row = buttonList.Count / MaxXButtons;

            return new Vector2(16 + (ButtonSize * (buttonList.Count % MaxXButtons)), ButtonMaxY - (row * ButtonSize));
        }

        public virtual void AddScreenButton(string screen, UIButton button)
        {
            if (!ScreenButtons.ContainsKey(screen))
                ScreenButtons.Add(screen, new List<UIButton>());

            button.Position = CalcButtonPos(button, ScreenButtons[screen]);
            button.OnClick = ScreenButtonClicked;
            ScreenButtons[screen].Add(button);
        }

        public virtual void ReWire(Game1 game)
        {
            Game = game;

            foreach (NodeConnection nc in Connections)
                nc.Game = Game;

            foreach (Software sw in Software)
                sw.Game = Game;

            foreach (UIButton button in Buttons)
            {
                button.Game = Game;
                button.OnClick = ButtonClicked;
            }

            foreach (string key in ScreenButtons.Keys)
            {
                foreach (UIButton button in ScreenButtons[key])
                {
                    button.Game = Game;
                    button.OnClick = ScreenButtonClicked;
                }
            }     
        }

        public virtual void UnWire()
        {
            foreach (UIButton button in Buttons)
                button.OnClick = null;

            foreach (string key in ScreenButtons.Keys)
            {
                foreach (UIButton button in ScreenButtons[key])
                    button.OnClick = null;
            }              
        }
    }
}
